module ActiveType

  module RecordExtension

    module Inheritance

      extend ActiveSupport::Concern

      included do
        class_attribute :extended_record_base_class
      end

      def self.add_foreign_key_option(extended_record_base_class, scope = nil, options = {})
        if scope.is_a?(Hash)
          options = scope
          scope = nil
        end
        unless options[:foreign_key] || options[:as]
          options = options.merge(foreign_key: extended_record_base_class.name.foreign_key)
        end
        [options, scope]
      end

      module ClassMethods

        def model_name
          @_model_name ||= begin
            if name
              # Namespace detection copied from ActiveModel::Naming
              namespace = module_ancestors.detect do |n|
                n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
              end
              # We create a Name object, with the sti class name, but self as the @klass reference
              # This way lookup_ancestors is invoked on the right class instead of the extended_record_base_class
              dup_model_name = ActiveModel::Name.new(self, namespace, sti_name)
              key = name.underscore.to_sym
              # We set the `i18n_key` to lookup on the derived class key
              # We keep the others the same to preserve parameter and route names
              dup_model_name.instance_variable_set(:@i18n_key, key)
              dup_model_name
            else # name is nil for the anonymous intermediate class
              extended_record_base_class.model_name
            end
          end
        end

        def module_ancestors
          if extended_record_base_class.respond_to?(:module_parents)
            extended_record_base_class.module_parents
          else
            extended_record_base_class.parents
          end
        end

        def sti_name
          extended_record_base_class.sti_name
        end

        def descends_from_active_record?
          extended_record_base_class.descends_from_active_record?
        end

        def has_many(name, scope=nil, *args, &extension)
          new_args, new_scope = Inheritance.add_foreign_key_option(extended_record_base_class, scope, *args)
          if new_scope.nil?
            super(name, **new_args, &extension)
          else
            super(name, new_scope, **new_args, &extension)
          end
        end

        def has_one(name, scope=nil, *args, &extension)
          new_args, new_scope = Inheritance.add_foreign_key_option(extended_record_base_class, scope, *args)
          if new_scope.nil?
            super(name, **new_args, &extension)
          else
            super(name, new_scope, **new_args, &extension)
          end
        end

        private

        # Rails 5 find_sti_class does a sanity check for proper inheritance that fails for
        # our usecase
        # copied from activerecord/lib/active_record/inheritance.rb
        def find_sti_class(type_name)
          type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
          subclass = begin
            if store_full_sti_class
              type_name.constantize
            else
              compute_type(type_name)
            end
          rescue NameError
            raise ActiveRecord::SubclassNotFound,
              "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
              "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
              "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
              "or overwrite #{name}.inheritance_column to use another column for that information."
          end
          #### our code starts here
          # Consider this class hierarchy
          # class Parent < ActiveRecord::Base; end
          # class Child < Parent; end
          # class ExtendedParent < ActiveType::Record[Parent]; end
          # class ExtendedChild < ActiveType::Record[Child]; end
          if self < subclass
            # i.e. ExtendendChild.find(child.id)
            # => self = ExtendedChild; subclass = Child
            # instantiate as ExtendedChild
            subclass = self
          elsif subclass < extended_record_base_class
            # i.e. ExtendedParent.find(child.id)
            # => subclass = Child; self = ExtendedParent; extended_record_base_class = Parent
            # There is no really good solution here, since we cannot instantiate as both ExtendedParent
            # and Child. We opt to instantiate as ExtendedParent, since the other option can be
            # achieved by using Parent.find(child.id)
            subclass = self
          end
          #### our code ends here
          unless subclass == self || descendants.include?(subclass)
            raise ActiveRecord::SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
          end
          subclass
        end

      end

    end

  end

end
